package com.server.questionbank.snframe.service.OllamaService.KnowledgeBaseService.TextEmbeddingService;

import com.fasterxml.jackson.databind.JsonNode;
import com.server.questionbank.snframe.config.ollama.OllamaConfig;
import com.server.questionbank.snframe.service.OllamaService.OllamaServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import com.hankcs.hanlp.seg.common.Term;
import com.hankcs.hanlp.tokenizer.StandardTokenizer;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.util.*;

@Service
public class TextEmbeddingServiceImpl implements TextEmbeddingService {
    private static final Logger logger = LoggerFactory.getLogger(TextEmbeddingServiceImpl.class);

    @Autowired // 注入 Ollama配置
    private OllamaConfig ollamaConfig;
    @Autowired // 注入 Web客户端
    private WebClient webClient;

    // 生成嵌入向量
    @Override
    public Mono<float[]> generateEmbedding(String text) {
        // 1. 构建请求体
        Map<String, Object> requestBody = Map.of(
                "model", ollamaConfig.getVectorModelName(),
                "prompt", text
        );

        // 2. 发送请求
        return webClient.post()
                .uri(ollamaConfig.getOllamaUrl() + "/api/embeddings")
                .contentType(MediaType.APPLICATION_JSON)
                .bodyValue(requestBody)
                .retrieve()
                .bodyToMono(EmbeddingResponse.class) // 直接映射为响应对象
                .map(EmbeddingResponse::getEmbedding)
                .onErrorResume(e -> {
                    logger.error("获取嵌入向量失败", e);
                    return Mono.error(new RuntimeException("获取嵌入向量失败"));
                });
    }

    // 余弦相似度计算
    @Override
    public float cosineSimilarity(float[] vec1, float[] vec2) {
        float dot = 0, norm1 = 0, norm2 = 0;
        for (int i = 0; i < vec1.length; i++) {
            dot += vec1[i] * vec2[i];
            norm1 += vec1[i] * vec1[i];
            norm2 += vec2[i] * vec2[i];
        }
        float denominator = (float) (Math.sqrt(norm1) * Math.sqrt(norm2));
        return denominator == 0 ? 0.0f : (dot / denominator);
    }

    // 计算关键词匹配分数
    @Override
    public float calculateKeywordScore(String correctAnswer, String userAnswer) {
        // 1. 提取关键词（结合预定义关键词和自动提取）
        Set<String> correctKeywords = extractKeywords(correctAnswer);

        // 2. 提取用户答案关键词
        Set<String> userKeywords = extractKeywords(userAnswer);

        // 3. 计算Jaccard相似度
        float keywordSimilarity = calculateJaccardSimilarity(correctKeywords, userKeywords);

        // 4. 计算关键短语匹配度
        float phraseScore = calculatePhraseMatchScore(correctAnswer, userAnswer);

        // 5. 综合评分（可调整权重）
        return 0.6f * keywordSimilarity + 0.4f * phraseScore;
    }

    // 计算Jaccard相似度
    private float calculateJaccardSimilarity(Set<String> set1, Set<String> set2) {
        if (set1.isEmpty() && set2.isEmpty()) return 1.0f;

        Set<String> intersection = new HashSet<>(set1);
        intersection.retainAll(set2);

        Set<String> union = new HashSet<>(set1);
        union.addAll(set2);

        return union.isEmpty() ? 0f : (float) intersection.size() / union.size();
    }

    // 短语匹配算法
    private float calculatePhraseMatchScore(String correct, String user) {
        // 1. 预处理：统一小写，去除前后空格
        correct = correct.toLowerCase().trim();
        user = user.toLowerCase().trim();

        // 2. 分句处理（考虑更多分隔符）
        String[] correctPhrases = correct.split("[,.!?;:，。！？；：、\\n\\r]+");
        String[] userPhrases = user.split("[,.!?;:，。！？；：、\\n\\r]+");

        // 3. 使用编辑距离进行模糊匹配
        int matches = 0;
        for (String cp : correctPhrases) {
            cp = cp.trim();
            if (cp.isEmpty()) continue;

            for (String up : userPhrases) {
                up = up.trim();
                if (up.isEmpty()) continue;

                // 改进匹配条件：包含或编辑距离小于阈值
                if (up.contains(cp) || calculateLevenshteinDistance(cp, up) <= 2) {
                    matches++;
                    break;
                }
            }
        }

        return correctPhrases.length > 0 ? (float) matches / correctPhrases.length : 0f;
    }

    // 计算Levenshtein编辑距离
    private int calculateLevenshteinDistance(String a, String b) {
        int[][] dp = new int[a.length()+1][b.length()+1];

        for (int i = 0; i <= a.length(); i++) dp[i][0] = i;
        for (int j = 0; j <= b.length(); j++) dp[0][j] = j;

        for (int i = 1; i <= a.length(); i++) {
            for (int j = 1; j <= b.length(); j++) {
                int cost = a.charAt(i-1) == b.charAt(j-1) ? 0 : 1;
                dp[i][j] = Math.min(Math.min(
                                dp[i-1][j] + 1,    // 删除
                                dp[i][j-1] + 1),    // 插入
                        dp[i-1][j-1] + cost // 替换
                );
            }
        }

        return dp[a.length()][b.length()];
    }

    // 分词器提取关键词
    @Override
    public Set<String> extractKeywords(String text) {
        Set<String> keywords = new HashSet<>();
        text = text.toLowerCase();

        // 分词
        List<Term> terms = StandardTokenizer.segment(text);

        Set<String> stopWordsZh = Set.of("的", "了", "是", "在", "和", "也", "就", "都", "而", "及", "与", "或", "一个", "没有");
        Set<String> stopWordsEn = Set.of("the", "and", "a", "an", "in", "on", "at", "is", "are", "was", "were", "this", "that");

        for (Term term : terms) {
            String word = term.word.trim();
            String nature = term.nature.toString();  // 词性标注，如：n, v, etc.

            if (word.length() <= 1) continue;

            // 英文词处理（词干提取）
            if (word.matches("[a-zA-Z]+")) {
                if (!stopWordsEn.contains(word)) {
                    keywords.add(word.replaceAll("(ing|ed|s|es)$", ""));
                }
            } else {
                // 中文词保留名词类（可根据 nature 控制粒度）
                if (!stopWordsZh.contains(word)) {
                    keywords.add(word);
                }
            }
        }

        return keywords;
    }

    // 嵌入向量响应对象
    private static class EmbeddingResponse {
        private float[] embedding;

        // getter and setter
        public float[] getEmbedding() {
            return embedding;
        }

        public void setEmbedding(float[] embedding) {
            this.embedding = embedding;
        }
    }
}
